想要 UI 出現時,不只是生硬地「出現」嗎?今天我們要來研究兩個 CSS 的新語法:@starting-style
和 transition-behavior: allow-discrete
。他們是專門解決那些從 display: none
這類狀態做成動畫的困境。
本篇同步發表於我的 Hashnode 部落格:
Eva Chen | 網頁設計師下班後 (hashnode.dev)
過去在網頁開發中,要為一個從 display: none
(完全隱藏)變成可見狀態(例如 display: block
)的元素加上轉場動畫,是一件不可能的事。
因為 display
屬性本身是「離散的 (discrete)」,意思是要嘛 none
要嘛 block
,沒有中間值可以讓瀏覽器去計算動畫的漸變過程。所以,當你改變 display
屬性時,UI 元素就會瞬間出現或消失,無法套用 transition
來產生平滑的動畫。
為了解決這個長久以來的難題,CSS 推出了 @starting-style
和 transition-behavior: allow-discrete
這兩個好幫手。
@starting-style
:定義動畫的「起點」@starting-style
是一個新的 CSS at-rule,它的作用是告訴瀏覽器,當一個 UI 元素首次渲染或從 display: none
狀態改變時,它的初始樣式是什麼。可以把它想像成是為動畫提供一個「前置狀態」或「起點」。
少了這個「起點」,瀏覽器就不知道該從何開始播放動畫,這就是為什麼以往直接對 display: none
的元素做轉場會失敗的原因。
@starting-style
?你可以將它寫在既有的 CSS 規則裡面,也可以獨立出來寫。為了確保起始樣式能被正確應用,建議將 @starting-style
寫在元素原本的樣式規則之後。
/* 獨立使用 */
@starting-style {
.my-element {
opacity: 0;
transform: scale(0.8);
}
}
.my-element {
opacity: 1;
transform: scale(1);
transition: opacity 0.5s, transform 0.5s;
}
/* 也可以巢狀在現有規則內 */
.my-element {
opacity: 1;
transform: scale(1);
transition: opacity 0.5s, transform 0.5s;
@starting-style {
opacity: 0;
transform: scale(0.8);
}
}
這樣的結果是:在這個 .my-element
載入時,會有一個淡出兼放大的動畫:
DEMO 連結:CSS @starting-style
transition-behavior: allow-discrete
:讓「離散」屬性動起來這個屬性的作用就如同它的名字一樣,允許 transition
去影響那些「離散的」CSS 屬性,其中最重要的就是 display
。
當你為 transition-property
加上 display
,並設定 transition-behavior: allow-discrete
後,瀏覽器就能夠讓轉場動畫能夠完整呈現。
transition-behavior
有兩個值:
normal
(預設值): 不會對離散屬性產生轉場效果。
allow-discrete
: 允許對離散屬性產生轉場效果。
那就讓我們來看一起搭配使用會怎麼樣?尤其是在處理像彈出視窗 (popover)、對話框 (dialog) 等需要顯示和隱藏的元件時。
讓我們來看一個實際的例子,我們來改寫之前寫過 :target
光箱的例子,不過改成用 JS 控制 Class 做狀態的調整。而 transition-behavior: allow-discrete
應用在 display: none
與 display: flex
的切換:
DEMO 連結:Pure CSS Lightbox by transition-behavior: allow-discrete
.lightbox {
/* 預設狀態:完全隱藏 */
display: none;
opacity: 0;
/* 設定過渡動畫 */
transition: opacity 0.3s;
transition-behavior: allow-discrete;
}
/* 開啟狀態:讓元素可見並完全不透明 */
.lightbox.is-open {
display: flex;
opacity: 1;
}
/* 關閉中狀態:讓元素保持可見,但變為透明以觸發退場動畫 */
.lightbox.is-closing {
display: flex; /* 關鍵:在動畫期間保持 display: flex */
opacity: 0;
}
/* 定義 .is-open 狀態的入場動畫起始點 */
@starting-style {
.lightbox.is-open {
opacity: 0;
}
}
點擊關閉按鈕:
JavaScript 執行,它會移除 .is-open
並新增 .is-closing
。
此時 Lightbox 的狀態從 opacity: 1
變為 opacity: 0
。
最關鍵的一步:因為 .is-closing
class 裡有 display: flex;
,所以 Lightbox 在動畫期間並不會馬上消失,它仍然佔據著頁面空間。
播放退場動畫:
opacity
的變化,並且因為元素仍然是 display: flex
,所以會順利地播放從 1 到 0 的淡出動畫。動畫結束:
JavaScript 中的 setTimeout 在等待了 300 毫秒後執行。
它會移除 .is-closing
class。
現在 Lightbox 身上沒有任何狀態 class,它的樣式退回到最原始的 .lightbox 規則,也就是 display: none,此時它才真正地從畫面中消失。
這兩個語法真的讓以前不可能做到的事做到了!
不過目前 Firefox 還未支援(transition-behavior: allow-discrete
的部分),但是還是可以值得期待!
詳細請看 Can I Use( @starting-style / transition-behavior: allow-discrete)。
延伸閱讀:
感謝看到最後的你,若你覺得獲益良多,請不要吝嗇給我按個喜歡。❤️
如果你喜歡我的創作,還想看看其他有趣的分享與日常,
可以追蹤我的 IG @im1010ioio,或者是🧋送杯珍奶鼓勵我,謝謝你🥰。